package com.shujia

object Demo06Func02 {
  /**
   * 函数式编程（高阶函数、面向函数）:
   * 1、以函数作为参数
   * 2、以函数作为返回值
   *
   * 面向函数：将函数传来传去，函数可以作为函数的参数或者是返回值，需要考虑函数的类型
   * 面向对象：将对象传来传去，对象可以作为函数的参数或者是返回值，需要考虑对象的类型
   *
   * 函数的类型怎么确定？
   * 函数的类型跟什么没有关系？
   * 1、跟def关键字无关
   * 2、跟函数的方法体没有直接关系，只需要保证返回值的类型一致即可
   * 3、跟函数名没有关系
   * 4、跟参数的名称没有关系
   *
   * 函数的类型由参数的类型（参数的个数以及顺序有关）以及返回值的类型有关
   *
   * 函数类型的声明：
   * 参数的类型 => 返回值类型
   * 如果有多个参数则：
   * (参数1的类型,参数2的类型,参数3的类型) => 返回值类型
   */

  // 这是一个“参数为String类型，返回值为Int类型”类型的函数
  // String => Int
  def func1(str1: String): Int = {
    str1.toInt + 100
  }

  // String => Int
  def func2(str2: String): Int = {
    str2.toInt + 200
  }

  /**
   * 定义一个函数funcX
   * 能够接收 像func1、func2这种类型 的函数 作为 funcX的参数，返回值类型为Int
   *
   * 面向函数编程之以函数作为参数
   */
  def funcX(f: String => Int): Int = {
    val f2Int: Int = f("200")
    f2Int + 300
  }

  // Int => Int
  def func3(int3: Int): Int = {
    int3 + 300
  }

  // String => String
  def func4(str4: String): String = {
    (str4.toInt + 300).toString
  }

  /**
   * 这是一个“参数分别为String类型以及Int类型，返回值也为Int类型”类型的函数
   * (String, Int) => Int
   */
  def func5(s1: String, i1: Int): Int = {
    s1.toInt + i1
  }

  // 将func1转换成匿名函数
  val func1_anonymous: String => Int = (str1: String) => {
    str1.toInt + 100
  }

  /**
   * 这是一个“参数只有一个为二元组(String,Int)，返回值为Int类型”类型的函数
   * ((String, Int)) => Int
   */
  def func6(t2: (String, Int)): Int = {
    t2._1.toInt + t2._2
  }

  def main(args: Array[String]): Unit = {


    println(funcX(func1)) // 600

    println(funcX(func2)) // 700
    //    funcX(func3)
    //    funcX(func4)

    /**
     * 使用匿名函数作为参数进行传递
     *
     */


    /**
     * 匿名函数的省略规则：
     * 1、如果代码只有一行，花括号可以省略
     * 2、如果匿名函数是作为参数传递给另一个函数，则匿名函数的参数类型可以省略
     * 3、在规则2的基础之上，如果去掉匿名函数的参数类型之后，参数只有一个，则括号可以省略
     * 4、如果参数只使用了一次则可以用下划线_替代，但需要把匿名函数的参数声明（即=>左边的部分全部省略）
     *
     */

    println(funcX(func1_anonymous))
    println(funcX((str1: String) => {
      str1.toInt + 100
    }))

    // 1、如果代码只有一行，花括号可以省略
    println(funcX((str1: String) => str1.toInt + 100))
    // 2、如果匿名函数是作为参数传递给另一个函数，则匿名函数的参数类型可以省略
    println(funcX((str1) => str1.toInt + 100))
    // 3、在规则2的基础之上，如果去掉匿名函数的参数类型之后，参数只有一个，则括号可以省略
    println(funcX(str1 => str1.toInt + 100)) // 一般省略到第三步即可
    // 4、如果参数只使用了一次则可以用下划线_替代，但需要把匿名函数的参数声明（即=>以及左边的部分）全部省略
    println(funcX(_.toInt + 100)) // 一般不推荐，除非在reduceByKey中 或者是使用_.split的时候

  }

}
